package connect4swt.gui;

import game.*;

import java.util.*;

import messages.InvalidInvitationReceivedMessage;
import messages.PlayerMoveMessage;
import messages.AccountCreationFailedMessage.ErrorCode;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.MessageBox;

import connect4client.core.*;
import core.PluginInfo;

/**
 * @author  Joel Cordeiro
 */
public class GUIMediatorImpl extends AbstractGUIMediator {
	
	public GUIMediatorImpl(Controller controller) {
		super(controller);
	}
	

	public void loginFailed() {
		LoginWindow.getInstance().showConnectionFailed();
	}


	public void loginSuccessful() {
		ClientWindow clientWindow = ClientWindow.getInstance();
		
		// Waits until ClientWindow is open
		synchronized(clientWindow.openMutex) {
			ClientWindow.getInstance().open();
			try {
				ClientWindow.getInstance().openMutex.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		LoginWindow.getInstance().dispose();
	}

	
	public void registrationSuccessful() {
		RegisterWindow.getInstance().dispose();
	}


	public void registrationFailed() {
		RegisterWindow.getInstance().showRegistrationFailed();
	}


	public void userLoggedIn(final Player player) {
		final ClientWindow clientWindow = this.clientWindow;

		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {
				clientWindow.receiveLoggedInMessage(player);
				clientWindow.addPlayerToPlayersList(player);
			}
		});
	}


	public void userLoggedOut(final String playerLogin) {
		final ClientWindow clientWindow = this.clientWindow;

		try {
			clientWindow.getDisplay().syncExec(new Runnable() {
				public void run() {
					clientWindow.receiveLoggedOutMessage(playerLogin);
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
	}


	public void listPlayersReply(final List<Player> players) {
		final ClientWindow clientWindow = this.clientWindow;

		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {
				for (Player player : players) {
					clientWindow.addPlayerToPlayersList(player);
				}
			}
		});
	}


	public void wall(final String senderLogin, final String contents) {
		final ClientWindow clientWindow = this.clientWindow;

		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {
				clientWindow.receiveWallMessage(senderLogin, contents);
			}
		});
	}


	public void invitationReceived(final String opponentName, final String gameName, final int replyId) {
		final ClientWindow clientWindow = this.clientWindow;

		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {
				clientWindow.invitationReceived(opponentName, gameName, replyId);
			}
		});
	}


	public void invitationAccepted(final Player opponent, final String gameName) {
		final ClientWindow clientWindow = this.clientWindow;

		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {
				clientWindow.opponentAcceptedInvitation(opponent, gameName);
			}
		});
	}


	public void invitationDenied(final Player opponent, final String gameName) {
		final ClientWindow clientWindow = this.clientWindow;

		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {
				clientWindow.opponentDeniedInvitation(opponent, gameName);
			}
		});
	}


	public void invalidInvitationReceived(final InvalidInvitationReceivedMessage.ErrorCode errorCode) {
		final ClientWindow clientWindow = this.clientWindow;
		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {
				clientWindow.invalidInvitationReceived(errorCode);
			}
		});
	}


	public void invitationTimedOut(final String login, final String gameType) {
		final ClientWindow clientWindow = this.clientWindow;

		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {
				clientWindow.receiveInvitationTimedOut(login, gameType);
			}
		});

	}


	public void matchStarted(final Player me, final Player opponent, final String gameName, final int matchId, final String nextPlayer) {
		final ClientWindow clientWindow = this.clientWindow;

		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {
				GameTab gameTab = clientWindow.matchStarted(me, opponent, gameName, matchId, nextPlayer);
				addGameTab(matchId, gameTab);
			}
		});
	}


	public void chat(final String login, final String contents, final int matchId) {
		final GameTab matchContainer = this.gameTabTable.get(matchId);

		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {

				String text1 = login + " says:\r\n";
				String text2 = contents + "\r\n";

				matchContainer.getTxtChat().append(text1);
				matchContainer.getTxtChat().append(text2);
			}
		});
	}


	public void playerMove(final PlayerMove playerMove, int matchId, final String nextPlayer) {		
		final ClientWindow clientWindow = this.clientWindow;		
		final Board board = getBoard(matchId);

		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {				
				board.setPlayerOneTurn(nextPlayer.equals(controller.getUser().getLogin()));
				board.fillPiece(playerMove);				
			}
		});
	}


	public void invalidMove(int matchId) {
		final ClientWindow clientWindow = this.clientWindow;
		final Board board = getBoard(matchId);

		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {
				board.setPlayerOneTurn(true);
				MessageBox msgBox = new MessageBox(board.getParentCanvas().getShell(), SWT.ICON_WARNING | SWT.OK);
				msgBox.setText("Warning");
				msgBox.setMessage("Invalid Move!");
				msgBox.open();
			}
		});
	}


	public void giveUp(int matchId) {
		final ClientWindow clientWindow = this.clientWindow;
		final GameTab matchContainer = this.gameTabTable.get(matchId);

		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {
				matchContainer.showGiveUp();
				matchContainer.removeFromTabFolder();
			}
		});
	}


	public void matchEnded(final int matchId, final List<Player> players, final String winner) {
		if (controller.getGameManager().containsMatch(matchId)) {
			final ClientWindow clientWindow = this.clientWindow;
			final GameTab gameTab = this.gameTabTable.get(matchId);
			
			clientWindow.getDisplay().syncExec(new Runnable() {
				public void run() {
					gameTab.showWinner(winner);
					gameTab.gameBoard.setMatchOver(true);
				}
			});
		}

		this.refreshRankings();
	}


	public void matchEndedTied(int matchId, List<Player> players) {
		if (controller.getGameManager().containsMatch(matchId)) {
			final ClientWindow clientWindow = this.clientWindow;
			final GameTab matchContainer = this.gameTabTable.get(matchId);
	
			clientWindow.getDisplay().syncExec(new Runnable() {
				public void run() {
					matchContainer.showMatchIsTied();
					matchContainer.gameBoard.setMatchOver(false);
				}
			});
		}

		this.refreshRankings();
	}
	
	
	public void listGamesReply(List<String> games) {
		for (String game : games) {
			System.err.println(" Jogo: " + game);
		}
	}
	
	
	public void accountCreationFailed(String adminLogin, String login, ErrorCode errorCode) {
		System.err.println("in GUIMediatorImpl::accountCreationFailed");
	}


	public void accountCreationSuccessful(String adminLogin, String login) {
		System.err.println("in GUIMediatorImpl::accountCreationSuccessful");
	}


	public void accountEditionFailed(String adminLogin, String login, messages.AccountEditionFailedMessage.ErrorCode errorCode) {
		System.err.println("in GUIMediatorImpl::accountEditionFailed");
	}


	public void accountEditionSuccessful(String adminLogin, String login) {
		System.err.println("in GUIMediatorImpl::accountEditionSuccessful");
	}


	public void accountRemotionFailed(String adminLogin, String login, messages.AccountRemotionFailedMessage.ErrorCode errorCode) {
		System.err.println("in GUIMediatorImpl::accountRemotionFailed");
	}


	public void accountRemotionSuccessful(String adminLogin, String login) {
		System.err.println("in GUIMediatorImpl::accountRemotionSuccessful");
	}


	public void invalidUserDetailsRequest(String login, messages.InvalidUserDetailsRequestMessage.ErrorCode errorCode) {
		System.err.println("in GUIMediatorImpl::invalidUserDetailsRequest");
	}


	public void listMatchsReply(List<MatchStatistics> activeMatchs) {
		System.err.println(activeMatchs.size() + " matchs");
		
		for (MatchStatistics match : activeMatchs) {
			System.err.println(match);
		}
	}


	public void userDetailsReply(Player player, List<MatchStatistics> oldMatchs, List<MatchStatistics> activeMatchs) {
		System.err.println("Details of " + player.getLogin());
		System.err.println(". password = " + player.getPassword());
		System.err.println(". ranking  = " + player.getRanking());
		System.err.println(". role     = " + player.getRole());
		System.err.println(". online?  = " + player.isOnline());
		
		System.err.println(activeMatchs.size() + " active matchs");
		
		for (MatchStatistics match : activeMatchs) {
			System.err.println(match);
		}
		
		System.err.println(oldMatchs.size() + " old matchs");
		
		for (MatchStatistics match : oldMatchs) {
			System.err.println(match);
		}
	}
	
	
	public void listPluginsReply(Collection<PluginInfo> plugins) {
		System.err.println("List of plugins: " + plugins.toString());
	}


	public void pluginShutdownFailed(String adminLogin, String pluginName) {
		System.err.println("Plugin shutdown failed: " + pluginName);		
	}


	public void pluginShutdownSuccessful(String adminLogin, String pluginName) {
		System.err.println("Plugin shutdown successful: " + pluginName);	
	}


	public void pluginStartupFailed(String adminLogin, String pluginName) {
		System.err.println("Plugin startup failed: " + pluginName);			
	}


	public void pluginStartupSuccessful(String adminLogin, String pluginName) {
		System.err.println("Plugin startup successful: " + pluginName);	
	}


	private void refreshRankings() {
		final ClientWindow clientWindow = this.clientWindow;
		final Collection<Player> players = controller.getPlayerManager().getPlayersMap().values();

		clientWindow.getDisplay().syncExec(new Runnable() {
			public void run() {
				clientWindow.refreshRankings(players);
			}
		});
	}


	private void addGameTab(int matchId, GameTab gameTab) {
		this.gameTabTable.put(matchId, gameTab);
	}


	private Board getBoard(int matchId) {
		GameTab gameTab = this.gameTabTable.get(matchId);
		return gameTab.getGameBoard();
	}

	private ClientWindow clientWindow = ClientWindow.getInstance();
	private Hashtable<Integer, GameTab> gameTabTable = new Hashtable<Integer, GameTab>();

	
}
